package com.wang.project.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wang.project.common.ErrorCode;
import com.wang.project.constant.InterfaceConstant;
import com.wang.project.constant.RemoteConstant;
import com.wang.project.constant.UserConstant;
import com.wang.project.exception.BusinessException;
import com.wang.project.model.dto.interfaceInfo.InterfaceInfoInvokeRequest;
import com.wang.project.model.entity.*;
import com.wang.project.model.enums.ApprovalStatusEnum;
import com.wang.project.service.*;
import com.wang.project.utils.ParamHandleUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RRateLimiter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class InvokeTestInterfaceServiceImpl implements InvokeTestInterfaceService {

    @Resource
    private InterfaceInfoService interfaceInfoService;

    @Resource
    private UserInterfaceInfoService userInterfaceInfoService;

    @Resource
    private UserService userService;

    @Resource
    private IntegralService integralService;

    @Resource
    private ApprovalService approvalService;

    @Autowired
    private RRateLimiter rateLimiter;

    //interface服务的nacos地址
    final String interfaceAddressPrefix = "http://openApi-interface/interface";

    final String nullParam = "";


    /**
     * 测试接口的调用地址为interface服务，直接服务间调用就可以 但是要进行限流
     * 所有interface表种的url地址存储应该就是interface实际接口地址
     * 接口信心应该分为
     */

    /**
     * 无参测试接口
     */
    public String unParamTestInterface(InterfaceInfo interfaceInfo) {
        String urlSuffix = interfaceInfo.getUrlSuffix();
        this.validInterfaceUrl(interfaceInfo.getUrl(), urlSuffix);
        String interfaceUrl = interfaceAddressPrefix + urlSuffix;
        //因为是没有参数的接口，所以都是get请求
        if (this.validFuncMethodPOST(interfaceInfo.getMethod())) {
            log.info("unParamTestInterface POST method interfaceUrl: {} ", interfaceUrl);
            /**
             * @RequestParam注解，并指定了required=false 则可以不用传递参数，默认为0或者null
             */
            return HttpUtil.post(interfaceUrl, nullParam);
        }
        log.info("unParamTestInterface GET method interfaceUrl: {} ", interfaceUrl);
        return HttpUtil.get(interfaceUrl);
    }

    /**
     * 有参测试接口
     *
     * @param interfaceInfo
     * @param param         接口参数
     * @return
     */
    public String paramTestInterface(InterfaceInfo interfaceInfo, String param) {
        String urlSuffix = interfaceInfo.getUrlSuffix();
        this.validInterfaceUrl(interfaceInfo.getUrl(), urlSuffix);
        String interfaceUrl = interfaceAddressPrefix + urlSuffix;
        if (validFuncMethodPOST(interfaceInfo.getMethod())) {
            //post请求
            Map<String, Object> paramMap = ParamHandleUtil.handleParam(param);
            log.info("paramTestInterface POST method interfaceUrl: {} paramMap:{}", interfaceUrl, JSONUtil.toJsonStr(paramMap));
            return HttpUtil.post(interfaceUrl, paramMap);
        }
        //get请求
        Map<String, Object> paramMap = ParamHandleUtil.handleParam(param);
        log.info("paramTestInterface GET method interfaceUrl: {} paramMap:{}", interfaceUrl, JSONUtil.toJsonStr(paramMap));
        return HttpUtil.get(interfaceUrl, paramMap);
    }

    /**
     * 测试接口
     * 调用次数接口也会消耗接口调用次数
     *
     * @param interfaceInfoInvokeRequest
     * @param request
     * @return
     */
    @Override
    public Object invokeInterfaceInfo(InterfaceInfoInvokeRequest interfaceInfoInvokeRequest, HttpServletRequest request) {
        /**
         * 接口分为两种，有参数接口 和 无参数接口
         * 一个接口要么有参数，要么无参数  暂定无法访问既有参数 又有有参数的接口
         * 也有可能一个接口 可以有参数 也可以没有参数
         */
        //校验接口
        Boolean validResult = interfaceInfoService.validTestInterface(interfaceInfoInvokeRequest, request);
        if (!validResult) {
            log.info("validTestInterface result err, interfaceInfoInvokeRequest: " + JSONUtil.toJsonStr(interfaceInfoInvokeRequest));
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "参数信息错误");
        }
        String result = null;

        //限流  每秒最大支持10次访问
        if (rateLimiter.tryAcquire()) {
            Long interfaceId = interfaceInfoInvokeRequest.getId();
            InterfaceInfo interfaceInfo = interfaceInfoService.getById(interfaceId);
            //判断是否有参数
            /**
             * 判断参数是否必填，这里不用做校验，只需要在interface服务的每一个接口方法中做参数的判断空就可
             */
            String userRequestParams = interfaceInfoInvokeRequest.getUserRequestParams();
            if (StringUtils.isBlank(userRequestParams)) {
                log.info("invokeInterfaceInfo unparam interfaceInfo:{} ", interfaceInfo);
                //无参测试接口
                result = this.unParamTestInterface(interfaceInfo);
            } else {
                //有参测试接口
                log.info("invokeInterfaceInfo param:{} interfaceInfo:{} ", userRequestParams, interfaceInfo);
                result = this.paramTestInterface(interfaceInfo, userRequestParams);
            }
            // 接口调用次数减1 如果是调用自己的接口，则不消耗调用次数
            User loginUser = userService.getLoginUser(request);
            Boolean updateResult = this.updateInterfaceInvokeNum(interfaceInfo, loginUser);
            if (!updateResult) {
                log.error("invokeInterfaceInfo updateInterfaceInvokeNum err interfaceId：{} ", interfaceId);
            }
            // 判断是否是用户上传的接口，如果是用户上传的接口，则用户可以获得对应积分
            this.updateUserIntegral(interfaceInfo, loginUser);
        } else {
            log.info("invokeInterfaceInfo 访问过于频繁,拒绝执行测试方法");
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "访问过于频繁");
        }
        return result;
    }

    /**
     * 判断是否是用户上传的接口，如果是用户上传的接口，则用户可以获得对应积分
     *
     * @param interfaceInfo
     * @return
     */
    public void updateUserIntegral(InterfaceInfo interfaceInfo, User user) {
        //先获取所有管理员的账号
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("userRole", UserConstant.ADMIN_ROLE);
        List<User> adminList = userService.list(userQueryWrapper);
        List<Long> adminUserIdList = adminList.stream().map(User::getId).collect(Collectors.toList());

        //判断接口的userId是否在adminUserIdList中
        if (!adminUserIdList.contains(interfaceInfo.getUserId())) {
            //用户调用的是其他用户发布的接口
            Long userId = interfaceInfo.getUserId();
            QueryWrapper<Integral> integralQueryWrapper = new QueryWrapper<>();
            integralQueryWrapper.eq("userId", userId);
            Integral integral = integralService.getOne(integralQueryWrapper);
            // 获取审核表中的integral字段
            int approvalIntegral = this.getApprovalIntegral(interfaceInfo);
            integral.setIntegralAmount(integral.getIntegralAmount() + approvalIntegral);
            integralService.updateById(integral);
        }
    }

    /**
     * 获取当前接口被调用获得的奖励积分值
     *
     * @param interfaceInfo
     * @return
     */
    public Integer getApprovalIntegral(InterfaceInfo interfaceInfo) {
        Approval approval = new Approval();
        approval.setApprovalStatus(ApprovalStatusEnum.APPROVAL_SUCCESS.getValue()); //审核通过的
        approval.setDescription(interfaceInfo.getDescription());
        approval.setMethod(interfaceInfo.getMethod());
        approval.setUrl(interfaceInfo.getUrl());
        approval.setRequestHeader(interfaceInfo.getRequestHeader());
        approval.setRequestParams(interfaceInfo.getRequestParams());
        approval.setResponseHeader(interfaceInfo.getResponseHeader());
        QueryWrapper<Approval> approvalQueryWrapper = new QueryWrapper<>(approval);
        Approval approvalServiceOne = approvalService.getOne(approvalQueryWrapper);
        return approvalServiceOne.getIntegral();
    }

    /**
     * 更新用户调用接口次数
     *
     * @param interfaceInfo
     * @param user
     * @return
     */
    public Boolean updateInterfaceInvokeNum(InterfaceInfo interfaceInfo, User user) {
        if (interfaceInfo.getUserId().equals(user.getId())) {
            //如果调用的是自己接口，则直接返回
            return true;
        }

        log.info("updateInterfaceInvokeNum interface : {} , user: {}", JSONUtil.toJsonStr(interfaceInfo), JSONUtil.toJsonStr(user));
        QueryWrapper<UserInterfaceInfo> userInterfaceInfoQueryWrapper = new QueryWrapper<>();
        userInterfaceInfoQueryWrapper.eq("interfaceId", interfaceInfo.getId());
        userInterfaceInfoQueryWrapper.eq("userId", user.getId());
        UserInterfaceInfo userInterfaceInfo = userInterfaceInfoService.getOne(userInterfaceInfoQueryWrapper);
        userInterfaceInfo.setLeftNum(userInterfaceInfo.getLeftNum() - 1);
        return userInterfaceInfoService.updateById(userInterfaceInfo);
    }

    /**
     * 校验接口地址是否合法
     *
     * @param url       接口地址全称
     * @param urlSuffix 接口地址后缀
     */
    public void validInterfaceUrl(String url, String urlSuffix) {
        if (url.isEmpty() || url == null) {
            log.info("validInterfaceUrl url is empty");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "接口地址不存在");
        }
        if (urlSuffix.isEmpty() || urlSuffix == null) {
            log.info("validInterfaceUrl urlSuffix is empty");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "接口地址不存在");
        }
    }

    //校验接口是否是POST方法
    public Boolean validFuncMethodPOST(String method) {
        String lowerCase = method.toLowerCase();
        return lowerCase.equals(InterfaceConstant.POST);
    }

    //校验接口是否GET方法
    public Boolean validFuncMethodGET(String method) {
        String lowerCase = method.toLowerCase();
        return lowerCase.equals(InterfaceConstant.GET);
    }
}
